﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Algobox.JuicyLib.Arithmetic
{
    public class DateTimeMovingMedian
    {
        /// <summary>
        /// Create a new moving median calculator.
        /// </summary>
        /// <param name="windowSize">The maximum number of values to be considered
        /// by this moving median calculation.</param>
        /// <exception cref="ArgumentOutOfRangeException">If windowSize less than one.</exception>
        public DateTimeMovingMedian(TimeSpan windowSize)
        {
            if (windowSize <= TimeSpan.Zero)
                throw new ArgumentOutOfRangeException("windowSize", windowSize, "Window size must be greater than zero.");

            Data = new Dictionary<DateTime, double>();
            WindowSize = windowSize;
        }

        Dictionary<DateTime, double> Data;

        TimeSpan WindowSize;

        double CurrentValue;

        public double Average { get { return CurrentValue; } }

        /// <summary>
        /// Updates the moving median with its next value, and returns the updated median value.
        /// When IsMature is true and NextValue is called, a previous value will 'fall out' of the
        /// moving average.
        /// </summary>
        public double NextValue(DateTime timeStamp, double nextValue)
        {
            Data[timeStamp] = nextValue;

            if (Data.Count == 1)
            {
                CurrentValue = nextValue;
            }
            else
            {
                Data = Data.Where(elements => elements.Key > timeStamp - WindowSize).ToDictionary(item => item.Key, item => item.Value);

                List<double> items = Data.OrderBy(item => item.Value).Select(item => item.Value).ToList();

                int medianIndex = items.Count / 2;
                // even count
                if (Data.Count % 2 == 0)
                {
                    CurrentValue = (items[medianIndex - 1] + items[medianIndex]) / 2;
                }
                else
                {
                    CurrentValue = items[medianIndex];
                }
            }
            return CurrentValue;
        }


        /// <summary>
        /// Clears any accumulated state and resets the calculator to its initial configuration.
        /// Calling this method is the equivalent of creating a new instance.
        /// </summary>
        public void Reset()
        {
            Data.Clear();
        }




    }





}
